package com.ruoyi.yf.tool.barcode;

import cn.hutool.crypto.SecureUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.security.PermissionUtils;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * XiaoYi...
 * 条形码生成规则
 * 2020-6-10
 */
public class Rules {

    private static final Logger log = LoggerFactory.getLogger(Rules.class);

    //默认值 标识
    private static final String key = "ls";

    public Rules() {
    }


    /**
     * 生成条码信息
     *
     * @param validation 是否验证
     * @return 返回条码
     */
    @Log(title = "生成条形码", businessType = BusinessType.BARCODE_ERR)
    public String barcode(boolean validation) {
        //生成新条形码
        String exchange = br();
        if (!validation && codeCheckLong(exchange)) {
            return null;
        }
        return exchange;
    }

    @Log(title = "生成条形码", businessType = BusinessType.BARCODE_ERR)
    public String barcode() {
        return barcode(true);
    }

    /**
     * 返回长度20位的新code条码，给批量上传使用
     *
     * @return
     */
    public String longBarcode() {
        return "yl" + barcode();
    }


    /**
     * 生成条码
     *
     * @param validation 条码验证
     * @param number     条码个数
     * @return 返回条码结合
     */
    @SneakyThrows
    @Log(title = "批量生成条码", businessType = BusinessType.BARCODE_list_ERR)
    public List<String> barcode(boolean validation, int number) {
        List<String> result = new ArrayList<>();
        for (int i = 0; i < number; i++) {
            Thread.sleep(1);
            result.add(barcode(validation) + "\r\n");
        }
        return result;
    }

    @Log(title = "批量生成条码", businessType = BusinessType.BARCODE_list_ERR)
    public List<String> barcode(int number) {
        return barcode(true, number);
    }


    /**
     * @return
     * @author XiaoYi...
     * 条形码 重复压力测试
     * @date 2020/5/9 17:28
     */
    public static void startTest() throws InterruptedException {
        HashSet<String> set = new HashSet();
        int i = 0;
        while (true) {
            String code = br();
            set.add(code);
            i++;
            if (set.size() != i) {
                log.info("第" + i + "次相同,存储：" + set.size() + "条，值为：" + code + "，尾部随机数为" + code.substring(10, 18));
//                startTest();
                break;
            }
            log.info(i + "条<>" + code);
        }
    }

    /**
     * @return
     * @author XiaoYi...
     * 生成22 位条形码
     * @date 2020/5/9 17:29
     * 1589061121168902212099
     * [ls, 15890, 61121, 16890221, 2, 0, 9, 9]
     * $1:标识
     * $2:13位时间戳前5位 (5位)
     * $3:随机排序数 (5位)
     * $4:13位时间戳前8位 (8位)
     * $5:固定位，偶数 (1位)
     * $6: md5($1+$2+$3)首位数字 (1位)
     * $7: md5($4+$5)首位数字 (1位)
     * $8: md5($2+$3+$4+$5)首位数字 (1位)
     * 合成 ：$2+$3+$4+$5+$6+$7+$8
     */
    public static String br() {
        //获取时间戳
        Long startTs = System.currentTimeMillis();
        String[] result = spitMillis(startTs + "");
        random_and_batch(result);
        position_(result);
        String exchange = exchange(result);
        return exchange;
    }


    /**
     * @param strMillis 分割时间戳字符串
     * @return
     * @author XiaoYi...
     * @date 2020/5/11 9:13
     */
    private static String[] spitMillis(String strMillis) {
        String[] result = new String[8];
        String prefix = strMillis.substring(0, 5);//前缀
        String suffix = strMillis.substring(5, strMillis.length());//后缀
        result[0] = key;
        result[1] = prefix;
        result[3] = suffix;
        return result;
    }

    /**
     * @param result 添加中间5随机数
     * @return
     * @author XiaoYi...
     * @date 2020/5/11 9:13
     */
    private static String[] random_and_batch(String[] result) {
        String substring = (System.nanoTime() + "").substring(8, 13);
        result[2] = substring;
        return result;
    }

    /**
     * @param result 添加固定位
     * @return
     * @author XiaoYi...
     * @date 2020/5/11 9:13
     */
    private static String[] position_(String[] result) {
        //平台生成
        boolean boo = true;
        while (boo) {
            int random = new Random(System.nanoTime()).nextInt(10); //0-9 随机数
            if (random % 2 == 0) {//偶数位
                boo = false;
//                System.out.println("偶数：" + random);
                result[4] = random + "";
            }
        }
        return result;
    }

    /**
     * @param result 添加md5 加密规则
     * @return 返回 规则条形码
     * @author XiaoYi...
     * @date 2020/5/11 9:11
     */
    private static String exchange(String[] result) {
        result[5] = first_No(result[0] + result[1] + result[2]);//前十位效验
        result[6] = first_No(result[3] + result[4]);//后所有位效验
        result[7] = first_No(result[1] + result[2] + result[3] + result[4]);//所有位效验

        String is = "";
        for (int i = 1; i < result.length; i++) {
            is += result[i];
        }
        //打印数组
//        log.info(Arrays.toString(result));
        //打印条码
        return is;
    }


    /**
     * @param str 加密字符串
     *            获取Md5加密后 第一位数字值
     * @return
     * @author XiaoYi...
     * @date 2020/5/11 9:12
     */
    private static String first_No(String str) {
        return SecureUtil.md5(str).replaceAll("[^(0-9)]", "").substring(0, 1);
    }


    /**
     * @param code 18位条形码
     *             18位 校验规则 码
     * @return
     * @author XiaoYi...
     * @date 2020/5/11 9:12
     */
    public static boolean codeCheck(String code) {
        if (code.length() != 18)
            return false;
        String f = code.substring(0, 10), l = code.substring(10, 15), r_1 = code.substring(15, 16), r_2 = code.substring(16, 17), r_3 = code.substring(17, 18);
        if (!first_No(key + f).equals(r_1))
            return false;
        else if (!first_No(l).equals(r_2))
            return false;
        else if (!first_No(f + l).equals(r_3))
            return false;
        else
            return true;
    }

    public static void main(String asg[]) throws InterruptedException {
//        startTest();

        boolean b = codeCheckLong(br());
//        System.out.println(b);
//        System.out.println(codeCheck("000317300187416669"));

    }

    /**
     * @param code 22位条形码
     *             规则加密效验
     * @return
     * @author XiaoYi...
     * @date 2020/5/11 9:12
     */
    public static boolean codeCheckLong(String code) {
        log.info(code);
        //验证长度
        if (code.length() != 22)
            return false;
        //固定位
        String position_ = code.substring(18, 19);
        //获取后三位加密值
        char[] chars = code.substring(19, code.length()).toCharArray();

//        System.out.println("固定位：" + position_);
//        System.out.println("校验位："+Arrays.toString(chars));

        //Md5加密效验第1位加密
        if (!first_No(key + code.substring(0, 10)).equals(chars[0] + ""))
            return false;
        //Md5加密效验第2位加密
        if (!first_No(code.substring(10, 19)).equals(chars[1] + ""))
            return false;
        //Md5加密效验第3位加密
        if (!first_No(code.substring(0, 19)).equals(chars[2] + ""))
            return false;
        return true;
    }

}
